#include "ParseXMLConfigFile.h"
#include "clcomidfactory.h"
#include <algorithm>
#include <QFile>
#include "clcomfuncs.h"

using namespace std;

CXMLTree::CXMLTree(QString name, void* pXMLFile, CXMLTree* pfather, bool broot)
{
    m_czName = name;
    m_bVRoot = false;
    if (broot)
    {
        m_bVRoot = true;
        m_czName = "root";
    }
    m_czID = CLComIDFactory::CreateID().c_str();
    m_pFather = pfather;
	_pXMLFile = pXMLFile;
}
string CXMLTree::GetID()
{
    return m_czID.toLatin1().data();
}
CXMLTree::~CXMLTree()
{
    auto it = m_mapIdChilds.begin();
    for (; it != m_mapIdChilds.end(); ++it)
	{
        ((CParseXMLConfigFile*)_pXMLFile)->DelPtree(
			it->second->GetID());
	}
    m_mapIdChilds.clear();
    m_mapNameChilds.clear();
}
CXMLTree* CXMLTree::GetNextNode(
	std::map<std::string, CXMLTree*>::iterator& it)
{
	++it;
    if (it== m_mapIdChilds.end())
	{
		return NULL;
	}
	return it->second;
}
CXMLTree* CXMLTree::GetFirstNode(
	std::map<std::string, CXMLTree*>::iterator& it)
{
    it = m_mapIdChilds.begin();
    if (it == m_mapIdChilds.end())
	{
		return NULL;
	}
	return it->second;
}
void CXMLTree::AddChild(CXMLTree* pt)
{
    pt->m_pFather = this;
	pt->_pXMLFile = this->_pXMLFile;
	string id = 
		pt->GetID();

    this->m_mapIdChilds.insert(
		pair<std::string, PTREE>(id, pt));

    m_mapNameChilds[pt->GetName()][id] = pt;

    ((CParseXMLConfigFile*)this->_pXMLFile)->Add2IDMaps(
        pt->GetID(),
		pt);
}
CXMLTree* CXMLTree::AddChild(QString namestr, QString valuestr)
{
    PTREE pt = new TREE(namestr, _pXMLFile, this);
    pt->SetValue(valuestr);

    this->m_mapIdChilds.insert(
		pair<std::string, PTREE>(
            pt->GetID(), pt));

    m_mapNameChilds[pt->GetName()][pt->GetID()] = pt;

    ((CParseXMLConfigFile*)this->_pXMLFile)->Add2IDMaps(
        pt->GetID(), pt);
	return pt;
}
CXMLTree* CXMLTree::AddChild(QString namestr, int nvalue)
{
    QString valuestr = QString("%1").arg(nvalue);

    return AddChild(namestr, valuestr);
}
CXMLTree* CXMLTree::AddChild(QString namestr, double dvalue)
{
    QString valuestr = QString("%1").arg(dvalue);

    return AddChild(namestr, valuestr);
}
QString CXMLTree::GetName()
{
    return m_czName;
}
QString CXMLTree::GetValue()
{
    return m_czValue;
}
int CXMLTree::GetIntValue()
{
    return m_czValue.toInt();
}
double CXMLTree::GetDoubleValue()
{
    return m_czValue.toDouble();
}
QString CXMLTree::GetCharsValue()
{
    return m_czValue;
}
QString CXMLTree::GetAttru(QString attname)
{
    auto it = m_mapAttributes.find(attname);
    if (it == m_mapAttributes.end())
    {
        return "";
    }
    return m_mapAttributes[attname];
}
QString CXMLTree::GetAttru()
{
    QString attstr = "";
    for (auto it = m_mapAttributes.begin();
         it != m_mapAttributes.end();
         )
    {
        attstr += it.key();
        attstr += "=";
        attstr += it.value();

        ++it;
        if (it != m_mapAttributes.end())
        {
            attstr += " ";
        }
    }
    return attstr;
}
int CXMLTree::AttruCount()
{
    return m_mapAttributes.size();
}
int CXMLTree::SetValue(QString valuestr)
{
    m_czValue = valuestr;
	return PXML_OK;
}
int CXMLTree::SetDValue(double dValue)
{
    m_czValue = QString("%1").arg(dValue);
	return PXML_OK;
}
int CXMLTree::SetNValue(int nValue)
{
    m_czValue = QString("%1").arg(nValue);
	return PXML_OK;
}
int CXMLTree::UpdateAttribute(QString attname, QString attributestr)
{
    if (attname.length() == 0 || attributestr.length() == 0)
    {
        return PXML_NO_VALUE;
    }
    m_mapAttributes[attname] = attributestr;
	return PXML_OK;
}
int CXMLTree::DelNode(string id)
{
    auto it = m_mapIdChilds.find(id);
    if (it != m_mapIdChilds.end())
	{
        ((CParseXMLConfigFile*)this->_pXMLFile)->DelPtree(
            it->second->GetID());

		auto itname = 
            m_mapNameChilds.find(it->second->GetName());;
        if (itname != m_mapNameChilds.end())
		{
			itname->second.erase(id);
		}

        m_mapIdChilds.erase(it);
		return PXML_OK;
	}
	return PXML_NO_VALUE;
}
int CXMLTree::DelNodeByName(QString name)
{
	auto itname =
        m_mapNameChilds.find(name);
    if (itname != m_mapNameChilds.end())
	{
		auto itnode = itname->second.begin();

		if (itnode != itname->second.end())
		{
			return DelNode(itnode->first);
		}
	}
	return PXML_NO_VALUE;
}
PTREE CXMLTree::GetNode(
	string id)
{
	auto it = 
        m_mapIdChilds.find(id);
    if (it != m_mapIdChilds.end())
	{
		return it->second;
	}
	for (
        it = m_mapIdChilds.begin();
        it != m_mapIdChilds.end(); ++it)
	{
		PTREE temp = it->second->GetNode(
			id);
		if (temp)
		{
			return temp;
		}
	}

	return NULL;
}
CXMLTree* CXMLTree::GetNodeByName(
    QString name)
{
    name = name.toLower();
	auto itname =
        m_mapNameChilds.find(name);

    if (itname != m_mapNameChilds.end())
	{
		if (itname->second.size() > 0)
		{
			auto itnode = itname->second.begin();

			return itnode->second;
		}
	}
	
    for (auto &child : m_mapIdChilds)
	{
		CXMLTree* pt =
			child.second->GetNodeByName(name);

		if (pt != nullptr)
		{
			return pt;
		}
	}
	return nullptr;
}
CXMLTree* CXMLTree::GetFather()
{
    return m_pFather;
}

CParseXMLConfigFile::CParseXMLConfigFile(string id) :m_pRoot(NULL)
{
    memcpy((void*)czID, (void*)id.c_str(), CLComFuncs::min(XML_ID_LEN, id.size()));
}
CParseXMLConfigFile::CParseXMLConfigFile()
{
    string id = CLComIDFactory::CreateID();
    memcpy((void*)czID, (void*)id.c_str(), CLComFuncs::min(XML_ID_LEN, id.size()));
}
int CParseXMLConfigFile::LoadXMLFile(QString fileName)
{
    QDomDocument mk;
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) return PXML_FILE_INVALID;

    QString error = "";
    int row = 0, column = 0;

    if(!mk.setContent(&file, false, &error, &row, &column))
    {
        file.close();
        return PXML_FILE_INVALID;
    }
    file.close();
    if (m_pRoot)
    {
        DelPtree(m_pRoot->GetID());
    }
    m_pRoot = new TREE("root", this,  nullptr, true);

    _mapIDTrees[m_pRoot->GetID()] = m_pRoot;

    EnumXml(mk);

    m_strFileName = fileName;

    return PXML_OK;
}
void CParseXMLConfigFile::parseAttribute(
        PTREE ptree,
        const QDomNamedNodeMap& attributemap)
{
    for (int i = 0; i < attributemap.count(); ++i)
    {
        QDomAttr attnode = attributemap.item(i).toAttr();
        if (!attnode.isAttr())
        {
            ptree->UpdateAttribute(attnode.name(), attnode.value());
        }
    }
}
void CParseXMLConfigFile::parseNode(
        void* pXMLFile,
        CXMLTree* pfather,
        const QDomNode& node)
{
    if(!node.isNull())
    {
       QDomElement element = node.toElement(); // try to convert the node to an element.
       if(!element.isNull())
       {
            PTREE ptree = new TREE(
                       element.tagName(),
                       pXMLFile,
                       pfather);

            QDomNamedNodeMap atts = element.attributes();
            parseAttribute(ptree, atts);
            pfather->AddChild(ptree);

            QDomNode nodeson = element.firstChild();
            while(!nodeson.isNull())
            {
              parseNode(pXMLFile, ptree, nodeson);
              nodeson = nodeson.nextSibling();
            }
       }
    }
}
void CParseXMLConfigFile::EnumXml(QDomDocument& xml)
{
    QDomElement root = xml.documentElement();

    QDomNode rootnode = root.toElement();

    parseNode(this, m_pRoot, rootnode);
}
bool CParseXMLConfigFile::CreateXMLFile(QString fileName)
{
	if (m_pRoot)
	{
		DelPtree(m_pRoot->GetID());
	}

    m_pRoot = new TREE("root", this, nullptr, true);

    _mapIDTrees[m_pRoot->GetID()] = m_pRoot;

	m_strFileName = fileName;

    return true;
}
void GetNodeStr(CXMLTree* pt, QString& str, int& nSpace)
{
	if (pt->m_bVRoot)
	{
        auto it = pt->m_mapIdChilds.begin();
        if (it != pt->m_mapIdChilds.end())
		{
			return GetNodeStr(
				it->second, 
				str, 
				nSpace);
		}
	}
	for (int i = 0; i < nSpace; ++i)
	{
        str += " ";
	}
    str += "<";
	str += pt->GetName();
	
    if (pt->AttruCount() > 0)
	{
        str += " ";
		str += pt->GetAttru();
	}
    str += ">";
    auto it = pt->m_mapIdChilds.begin();
    if (it == pt->m_mapIdChilds.end())
	{
		str += pt->GetValue();
	}
	else
	{
        str += "\n";
        for (; it != pt->m_mapIdChilds.end(); ++it)
		{
			GetNodeStr(it->second, str, ++nSpace);

			--nSpace;
		}
		for (int i = 0; i < nSpace; ++i)
		{
            str += " ";
		}
	}
    str += "</";
	str += pt->GetName();
    str += ">\n";
}
int CParseXMLConfigFile::Save2XMLFile()
{
	if (!m_pRoot)
	{
		return PXML_NO_VALUE;
	}
    QString buffer = "<?xml version =\"1.0\" encoding=\"UTF-8\"?>\n";
	
	int nSpace = 0;

    GetNodeStr(m_pRoot, buffer, nSpace);

    QFile file(m_strFileName);

    file.open(QIODevice::WriteOnly);

    if (!file.isOpen())
    {
		return 0;
	}

    return file.write(buffer.toLatin1().data());
}
bool CParseXMLConfigFile::ReloadFromXMLFile()
{
	return LoadXMLFile(m_strFileName);
}
string CParseXMLConfigFile::GetID()
{
	return czID;
}
CParseXMLConfigFile::~CParseXMLConfigFile()
{
    delete m_pRoot;
    m_pRoot = nullptr;
}
QString CParseXMLConfigFile::GetConfigFileName()
{
    return m_strFileName;
}
PTREE CParseXMLConfigFile::GetNodeByName(QString nodeName)
{
	if (!m_pRoot)
	{
		return NULL;
	}
    nodeName = nodeName.toLower();

    return m_pRoot->GetNodeByName(nodeName);
}
PTREE CParseXMLConfigFile::GetRootNode()
{
	if (!m_pRoot)
	{
		return NULL;
	}
	return m_pRoot;
}
void CParseXMLConfigFile::ClearIDMaps()
{
	_mapIDTrees.clear();
}
void CParseXMLConfigFile::Add2IDMaps(string id, PTREE pt)
{
	_mapIDTrees[id] = pt;
}
void CParseXMLConfigFile::DelPtree(string id)
{
	auto it = _mapIDTrees.find(id);
	if (it != _mapIDTrees.end())
	{
		delete it->second;
		it->second = nullptr;

		_mapIDTrees.erase(it);
	}
}
PTREE CParseXMLConfigFile::GetNodeByID(string id)
{
	if (id == "" || id == "root" || id == "ROOT")
	{
		auto it = _mapIDTrees.find(m_pRoot->GetID());
		if (it != _mapIDTrees.end())
		{
			return it->second;
		}
		return NULL;
	}
	auto it = _mapIDTrees.find(id);
	if (it != _mapIDTrees.end())
	{
		return it->second;
	}
	return NULL;
}
